package com.xand.bt.terminal.btce.connector;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.xand.bt.terminal.btce.connector.market.MarketLevel;
import com.xand.bt.terminal.btce.connector.market.MarketStore;
import com.xand.bt.terminal.btce.connector.market.MarketTicker;
import com.xand.bt.terminal.common.util.NetHelper;

public class MarketConnectorThread implements Runnable {

	private static final int SLEEP_INTERVAL = 1000;
	
	private Thread thread;
	
	private boolean running = false;
	
	private ConnectorConfig connectorConfig;
	
	private MarketStore marketStore;
	
	public MarketConnectorThread(ConnectorConfig connectorConfig, MarketStore marketStore) {
		
		this.thread = new Thread(this, "BTCE_CONNECTOR");
		
		this.connectorConfig = connectorConfig;
		this.marketStore = marketStore;
		
	}
	
	public void run() {
		
		while(this.running) {
	
			String[] references = this.connectorConfig.getReferences();
			
			for(String reference : references) {
				
				String depthText = this.fetchDepth(reference);
				String tradesText = this.fetchTrades(reference);
				String tickerText = this.fetchTicker(reference);
				
				try {
					this.processDepth(depthText, reference, MarketLevel.DIRECTION_ASK);
					this.processDepth(depthText, reference, MarketLevel.DIRECTION_BID);
					
				}catch(JSONException e){
					throw new RuntimeException(e);
				}
				
			}
			
			try {
				Thread.sleep(SLEEP_INTERVAL);
			}catch(InterruptedException e){
				throw new RuntimeException(e);
			}
			
		}
		
	}
	
	public void start() {
		this.running = true;
		
		this.thread.start();
	}
	
	public void stop() {
		this.running = false;
	}
	
	private void processTicker(String json, String reference) throws JSONException {
		
		JSONObject jsonObject = new JSONObject(json).getJSONObject("ticker");
		
		long lHigh = Math.round(jsonObject.getDouble("high") * 1000000);
		
		long lLow = Math.round(jsonObject.getDouble("low") * 1000000);
		
		long lAvg = Math.round(jsonObject.getDouble("avg") * 1000000);
		
		long lVol = jsonObject.getLong("vol");
		
		long lVolCur = Math.round(jsonObject.getDouble("vol_cur") * 100000000);
		
		long lLast = Math.round(jsonObject.getDouble("last") * 1000000);
		
		long lBuy = Math.round(jsonObject.getDouble("buy") * 1000000);
		
		long lSell = Math.round(jsonObject.getDouble("sell") * 1000000);
		
		MarketTicker marketTicker = new MarketTicker(
				reference, 
				lHigh, 
				lLow, 
				lAvg, 
				lVol, 
				lVolCur, 
				lLast, 
				lBuy, 
				lSell);
		
		
		
	}
	
	private void processDepth(String json, String reference, int direction) throws JSONException {
		JSONObject jsonObject = new JSONObject(json);

		Map<Long, Long> processedMarketLevels = new HashMap<Long, Long>();
		
		String sJsonArray;
		
		if(direction == MarketLevel.DIRECTION_ASK) {
			sJsonArray = "asks";
		} else {
			sJsonArray = "bids";
		}
		
		JSONArray jLevels = jsonObject.getJSONArray(sJsonArray);
		for(int i = 0; i < jLevels.length(); i++) {
			
			JSONArray singleAsk = jLevels.getJSONArray(i);
			
			double dPrice = singleAsk.getDouble(0);
			double dVolume = singleAsk.getDouble(1);
			
			double drPrice = dPrice * 1000000.0;
			long lPrice = Math.round(drPrice);
			
			double drVolume = dVolume * 100000000.0;
			long lVolume = Math.round(drVolume);
			
			// does this market level exists??
			MarketLevel test = this.marketStore.getMarketLevel(reference, direction, lPrice);
			if(test == null) {
				
				// create new level
				MarketLevel marketLevel = new MarketLevel(reference, lPrice, lVolume, direction);
				this.marketStore.addMarketLevel(marketLevel);
				
			} else {
				
				// should I update volume?
				if(test.getVolume() != lVolume) {
					this.marketStore.updateMarketLevelVolume(reference, direction, lPrice, lVolume);
				}
			}
			
			processedMarketLevels.put(lPrice, lPrice);
			
		}
		
		List<Long> levelsToRemove = new ArrayList<Long>();
		for(long l : this.marketStore.getPricesForReferenceAndDirection(reference, direction)) {
			
			Long test = processedMarketLevels.get(l);
			
			if(test == null) {
				levelsToRemove.add(l);
			}
			
		}
		
		// remove
		for(long l : levelsToRemove) {
			this.marketStore.removeMarketLevel(reference, direction, l);
		}
		
	}
	
	private String fetchDepth(String reference) {
		
		Map<String, String> depthUrls = this.connectorConfig.getDepthUrl();
		String depthUrl = depthUrls.get(reference);
		
		return NetHelper.fetchUrlAsString(depthUrl);
	}
	
	private String fetchTrades(String reference) {
		
		Map<String, String> tradesUrls = this.connectorConfig.getTradesUrl();
		String tradesUrl = tradesUrls.get(reference);
		
		return NetHelper.fetchUrlAsString(tradesUrl);
	}
	
	private String fetchTicker(String reference) {
		
		Map<String, String> tickerUrls = this.connectorConfig.getTickerUrl();
		String tickerUrl = tickerUrls.get(reference);
		
		return NetHelper.fetchUrlAsString(tickerUrl);
	}
	
}
